/*file chiron src/sk89q.therisenrealm.com/play.js */

/*preamble-sk89q

    Written by sk89q <http://sk89q.therisenrealm.com>
    Copyright 2008 sk89q. All rights reserved.

    Redistribution and use in source and binary forms, with or without 
    modification, are permitted provided that the following conditions 
    are met:
    
    1. Redistributions of source code must retain the above 
       copyright notice, this list of conditions and the following 
       disclaimer.
    2. Redistributions in binary form must reproduce the above
       copyright notice, this list of conditions and the following 
       disclaimer in the documentation and/or other materials provided 
       with the distribution.
    
    THIS SOFTWARE IS PROVIDED BY SK89Q "AS IS" AND ANY EXPRESS 
    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL SK89Q BE LIABLE FOR ANY DIRECT, 
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/

include('browser.js');
include('base.js');
publish(include('./wav.js'));

// use two players at once
var playersElement = tag('div', {
    'style': {
        'position': 'absolute',
        'visibility': 'hidden',
        'height': 0,
        'width': 0,
        'top': 0,
        'left': 0,
    }
});
document.body.appendChild(playersElement);

var players = [];
var playback;

/*** noteLengths
*/
exports.noteLengths = {
    'whole': 1,
    'half': .5,
    'quar': .25,
    'eighth': .128,
    'sixteenth': .064
};

/*** playUri
*/
exports.playUri = function (dataUri, duration) {
    if (no(duration))
        duration = 1;
    var now = new Date().getTime();
    var player = tag('embed', {
        'src': dataUri,
        'autostart': true
    });
    player.end = now + duration;
    players.push(player);
    playersElement.appendChild(player);
    for (var i = 0; i < players.length; i++) {
        var player = players[i];
        if (player.end < now) {
            player.parentNode.removeChild(players.shift());
        }
    }
};

/*** playSin
*/
exports.playSin = function (frequency, duration) {
    return playUri(sinWavUri(frequency, duration), duration);
};

/*** playSong
*/
exports.playSong = function (lines) {
    lines = iter(lines);

    var go = function () {
        try {
            var line = lines.next();
            playSin(line[0], line[1]);
            setTimeout(go, line[1]);
        } catch (exception) {
            if (isInstance(exception, StopIteration)) {
            } else {
                throw exception;
            }
        }
    };

    go();

    return {
        'stop': function () {
            lines = iter();
        }
    };

};

/*** play
*/
exports.play = function (song, options) {
    var song = parseSong(song);
    if (song.errors.length)
        return song.errors;
    playback = playSong(song.lines);
    return [];
};

/*** stop
*/
exports.stop = function (f) {
    playback.stop();
};

/*** parseSong
*/
exports.parseSong = function (song) {
    var tempo = 60 * 1000 / 120;
    var meter = 4;
    var songLines = [];

    var lines = song.split("\n");
    var errors = []; // parsing errors

    for (var i in lines) {
        var line = lines[i].replace(/^\s+|\s+$/g, '');
        if (line == "") { continue; }
        
        // comment
        if (line.charAt(0) == '#') {
        // instruction
        } else if (line.charAt(0) == '@') {
            var instruction = line.substr(1).split(/[\t ]+/);
            
            if (instruction[0] == "tempo") {
                tempo = 60 * 1000 / +instruction[1];
            }
        // note
        } else {
            var match = line.match(/^(\-?[0-9]+)[\t ]+([A-Ga-g]#?)[\t ]+(.+)$/, line);

            if (match) {                
                try {
                    var octave = +(match[1]);
                    var note = match[2];
                    var freq = frequency(octave, note);
                } catch (e) {
                    errors[errors.length] = 'Bad note specification at line #' + (i + 1) + ': ' + line;
                    continue;
                }
                
                try {
                    var noteLengthTexts = match[3].split("+");
                    
                    var noteLength = 0;
                    for (var t in noteLengthTexts) {
                        var noteLengthText = noteLengthTexts[t].replace(/^\s+|\s+$/g, '');
                        if (noteLengthText.charAt(noteLengthText.length-1) == ".") {
                            var l = noteLengthText.substr(0, noteLengthText.length-1);
                        } else {
                            var l = noteLengthText;
                        }
                        
                        if (noteLengths[l]) {
                            noteLength += tempo * noteLengths[l] * meter;
                        } else {
                            throw new Error("Unknown "+l+" note length specification");
                        }
                    }
                } catch (e) {
                    errors[errors.length] = 'Bad note length specification at line #' + (i + 1) + ': ' + line;
                    continue;
                }
                
                songLines[songLines.length] = [freq, noteLength];
            } else {
                errors[errors.length] = 'Parse error at line #' + (i + 1) + ': ' + line;
            }
        }
    }

    return {
        'lines': songLines,
        'errors': errors
    };

};


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

